package model;

public class RoffVector3D implements Cloneable {
	public static final int X=1; 
	public static final int Y=2; 
	public static final int Z=3; 

    public double x;
    public double y;
    public double z;

    public RoffVector3D() {
        x = 0;
        y = 0;
        z = 0;
    }
    public double getComponent(int code){
    	switch(code){
    	case X:
    		return x; 
    	case Y:
    		return y; 
    	case Z:
    		return z; 
    	}
    	throw new RuntimeException("code "+code+" is not defined!"); 
    }
    public RoffVector3D(RoffVector3D rhs) {
       this.x = rhs.x;
       this.y = rhs.y;
       this.z = rhs.z;
    }

    public RoffVector3D(double x, double y, double z) {
        this.x = x;
        this.y = y;
        this.z = z;
    }

    public RoffVector3D add(RoffVector3D rhs) {
        return (new RoffVector3D(x + rhs.x, y + rhs.y, z + rhs.z));
    }

    public void incBy(RoffVector3D rhs) {
        this.x += rhs.x;
        this.y += rhs.y;
        this.z += rhs.z;
    }

    public RoffVector3D sub(RoffVector3D rhs) {
        return (new RoffVector3D(x - rhs.x, y - rhs.y, z - rhs.z));
    }

    public void decBy(RoffVector3D rhs) {
        this.x -= rhs.x;
        this.y -= rhs.y;
        this.z -= rhs.z;
    }

    public RoffVector3D scalarMultiply(double scalar) {
        return (new RoffVector3D(x * scalar, y * scalar, z * scalar));
    }
    public String toString(){
    	return x+","+y+","+z; 
    }
    public void scalarMultiplyBy(double scalar) {
        x *= scalar;
        y *= scalar;
        z *= scalar;
    }

    public RoffVector3D scalarDivide(double scalar) {
        return (new RoffVector3D(x / scalar, y / scalar, z / scalar));
    }

    public void scalarDivideBy(double scalar) {
        x /= scalar;
        y /= scalar;
        z /= scalar;
    }

    public RoffVector3D cross(RoffVector3D rhs) {
        RoffVector3D rval = new RoffVector3D();

        rval.x = (y * rhs.z) - (z * rhs.y);
        rval.y = (z * rhs.x) - (x * rhs.z);
        rval.z = (x * rhs.y) - (y * rhs.x);
        return rval;
    }

    public double getLength() {
        return Math.sqrt((x * x) + (y * y) + (z * z));
    }

    public double getSquaredLength() {
        return ((x * x) + (y * y) + (z * z));
    }

    public double dot(RoffVector3D rhs) {
	return (x * rhs.x + y * rhs.y + z * rhs.z);
    }

    public void normalize() {
       double length = getLength();
       x = x / length;
       y = y / length;
       z = z / length;
    }

    public void rotateX(double degrees) {
        double radians = degrees * Math.PI / 180.0;
        double cosAngle = Math.cos(radians);
        double sinAngle = Math.sin(radians);
        double origY = y;
        y =	y * cosAngle - z * sinAngle;
        z = origY * sinAngle + z * cosAngle;
    }

    public void rotateY(double degrees) {
        double radians = degrees * Math.PI / 180.0;
        double cosAngle = Math.cos(radians);
        double sinAngle = Math.sin(radians);
        double origX = x;
        x =	x * cosAngle + z * sinAngle;
        z = z * cosAngle - origX * sinAngle;
    }

    public void rotateZ(double degrees) {
        double radians = degrees * Math.PI / 180.0;
        double cosAngle = Math.cos(radians);
        double sinAngle = Math.sin(radians);
        double origX = x;
        x =	x * cosAngle - y * sinAngle;
        y = origX * sinAngle + y * cosAngle;
    }

    public RoffVector3D rotate(RoffVector3D axis, double degrees) {
	// Formula goes CCW around axis. I prefer to think in terms of CW
	// rotations, to be consistant with the other rotation methods.
	double radians = -degrees * Math.PI / 180.0;
	double cosAngle = Math.cos(radians);
	double sinAngle = Math.sin(radians);

        RoffVector3D rval = new RoffVector3D();
	RoffVector3D v = this;
	RoffVector3D w = new RoffVector3D(axis);
	w.normalize();
	double vDotW = v.dot(w);
	RoffVector3D vCrossW = v.cross(w);
	w.scalarMultiplyBy(vDotW); // w * (v . w)

	rval.x = w.x + (v.x - w.x) * cosAngle + vCrossW.x * sinAngle;
	rval.y = w.y + (v.y - w.y) * cosAngle + vCrossW.y * sinAngle;
	rval.z = w.z + (v.z - w.z) * cosAngle + vCrossW.z * sinAngle;

        return rval;
    }

	public double getX() {
		return x;
	}

	public double getY() {
		return y;
	}

	public double getZ() {
		return z;
	}
}